﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Effects.Effects
{
    /// <summary>
    ///  Delegate for <see cref="E:Effects.Effects.Effect.SettingsChanged"/> event handlers.
    /// </summary>
    /// <param name="sender"> Object that has raised the event. </param>
    /// <param name="e"> Event argumetns. </param>
    public delegate void EffectSettingsChangedDelegate(object sender,EffectSettingsChangedEventArgs e);

    /// <summary>
    ///  Enumaration of valid settings types.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   Setting's type reprezents what values are allowed. 
    ///   Integer - integer number.
    ///   Real - real number.
    ///   List - list of values.
    ///  </para>
    /// </remarks>
    public enum ParameterType
    {
        /// <summary>
        ///  Integer value type parameter.
        /// </summary>
        Integer, 

        /// <summary>
        ///  Real value type parameter.
        /// </summary>
        Real, 

        /// <summary>
        ///  Enumeration type parameter.
        /// </summary>
        List
    }

    /// <summary>
    ///  One item in the list of allowed values, when SettingType is List.
    /// </summary>
    public struct ListItem
    {
        /// <summary>
        ///  String that is displayed to the user.
        /// </summary>
        private string displayName;
        /// <summary>
        ///  Gets the displayed string.
        /// </summary>
        public string DisplayName
        {
            get { return displayName; }
        }

        /// <summary>
        ///  Value that is sent to the effect.
        /// </summary>
        private object value;

        /// <summary>
        ///  Gets the value.
        /// </summary>
        public object Value
        {
            get { return value; }
        }

        /// <summary>
        ///  Creates new ListItem with given name and value.
        /// </summary>
        /// <param name="name"> Description of the value. It is displayed to the user. </param>
        /// <param name="value"> Value coresponding with the description </param>
        public ListItem(string name, object value)
        {
            this.displayName = name;
            this.value = value;
        }
    }

    /// <summary>
    ///  Represents one of effects settings.
    /// </summary>
    /// <remarks>
    ///   <para>
    ///    Instance of this class describes one parameter of the effect. There are three different types of parameters: Integer valuse, Real values and Enumeration.
    ///    Every parameter has it's own key - object holding unique value, so effect is able to determine which parameter it is,
    ///    and name - string describing the name of the parameter (this string is usually displayed to the user.
    ///   </para>
    ///   <para>
    ///    Moreover Integer and Real value parameters are described by boundary values that define interval of accepted values.
    ///    Enumeration type is define by array of <see cref="S:Effects.Effects.ListItem"/> that contains accepted values.
    ///   </para>
    /// </remarks>
    public struct EffectParam
    {
        /// <summary>
        ///  Name of the setting.
        /// </summary>
        string name;

        /// <summary>
        ///  Gets the name of the setting.
        /// </summary>
        public string Name
        {
            get { return name; }
        }

        /// <summary>
        ///  Key for the setting.
        /// </summary>
        object key;

        /// <summary>
        ///  Gets the key for the setting.
        /// </summary>
        public object Key
        {
            get { return key; }
        }

        /// <summary>
        ///  Type of the value.
        /// </summary>
        ParameterType valueType;

        /// <summary>
        ///  Gets the type of the parameter's value
        /// </summary>
        public ParameterType ValueType
        {
            get { return valueType; }
        }

        /// <summary>
        ///  Bounds for the value.
        /// </summary>
        /// <remarks>
        /// <para>
        ///  In fact meaning of this field is more general that just simply bounds for the value. 
        ///  For valueType == Integer or Real it is array (lenght 2), and bounds[0] is lower bound and bounds[1] is upper bound.
        /// </para>
        /// <para>
        ///  For valueType == List it is array of ListItem that represents acceptable values. Therefore it can be understood as some kind of bounds for the value.
        /// </para>
        /// </remarks>
        object bounds;

        /// <summary>
        ///  Returns bounds for the value of parameter.
        /// </summary>
        public object Bounds
        {
            get { return bounds; }
        }

        /// <summary>
        ///  Creates class describing parameter of integer value type.
        /// </summary>
        /// <param name="key"> Key of the parameter. </param>
        /// <param name="name"> Name of the parameter. </param>
        /// <param name="lowerBound"> Lower border of accepted interval.  </param>
        /// <param name="upperBound"> Upper boundary of accepted interval. </param>
        public EffectParam(object key,string name, int lowerBound, int upperBound)
        {
            this.valueType = ParameterType.Integer;
            this.name = name;
            this.key = key;

            int[] bounds = new int[2];
            bounds[0] = lowerBound;
            bounds[1] = upperBound;
            this.bounds = bounds;
        }

        /// <summary>
        ///  Creates class describing parameter of real value type.
        /// </summary>
        /// <param name="key"> Key of the parameter. </param>
        /// <param name="name"> Name of the parameter. </param>
        /// <param name="lowerBound"> Lower border of accepted interval.  </param>
        /// <param name="upperBound"> Upper boundary of accepted interval. </param>
        public EffectParam(object key, string name, double lowerBound, double upperBound)
        {
            this.valueType = ParameterType.Real;
            this.name = name;
            this.key = key;

            double[] bounds = new double[2];
            bounds[0] = lowerBound;
            bounds[1] = upperBound;
            this.bounds = bounds;
        }


        /// <summary>
        ///  Creates class describing parameter of enumeration (list) type.
        /// </summary>
        /// <param name="key"> Key of the parameter. </param>
        /// <param name="name"> Name of the parameter. </param>
        /// <param name="values"> Accepted values. </param>
        public EffectParam(object key, string name, ListItem[] values)
        {
            this.valueType = ParameterType.List;
            this.name = name;
            this.key = key;
            this.bounds = values;
        }

        /// <summary>
        ///  Descrption of the parameter. Returns name of the parameter.
        /// </summary>
        /// <returns> Name of the described parameter. </returns>
        public override string ToString()
        {
            return this.name;
        }
    }
   
    
    /// <summary>
    ///  Base class for effects.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   New effect can be easily created by deriving this class and overriding it's methods and properties.
    ///  </para>
    ///  <para>
    ///   Methods for modifying input signal:
    ///  </para>
    ///  <para>
    ///   Effecting the signal is done by <see cref="Effect.effect"/> method. That's probably the most important method of the whole class and
    ///   it is always necessary to overridde it in derived classes. Input if this method is array of doubles from interval [-1;1].
    ///   </para>
    ///   <para>
    ///    <see cref="Effect.Reset"/> method provides reseting of the effect. It is not necesary to override.
    ///  </para>
    ///  <para>
    ///   Technical properties:
    ///    There are few properties that cooperate between effect and workspace.
    ///    <para>
    ///      <see cref="Effect.Name"/> - gets name of the effect. This name is used to uniquely identify effect, therefore it must be overridden.
    ///    </para>
    ///    <para>
    ///     <see cref="Effect.DataSize"/> - gets or sets the size of array passed to <see cref="M:Effects.Effects.Effect.effect"/> method. It is not necessary to overrride this property.
    ///    </para>
    ///    <para>
    ///     <see cref="Effect.SampleRate"/> - gets or sets sampling rate of signal passed to <see cref="M:Effects.Effects.Effect.effect"/> method. This property is usually overrridden.
    ///    </para>
    ///    <para>
    ///      <see cref="Effect.Selected"/> - gets or sets selected indication if the effect. It is recomended no to change this property.
    ///    </para>
    ///    <para>
    ///      <see cref="Effect.Switch"/> - turn effect on/off. This property is usually overrridden.
    ///    </para>
    ///  </para>
    ///  <para>
    ///   Painting:
    ///    To highlight the fact that effect is selected <see cref="M:Effect.OnPaint"/> method uses hatched brush. By overriding this method another way of
    ///    highlighting can be implemented
    ///  </para>
    ///  <para>
    ///   Settings:
    ///   Application support recording changes of the parameter settings done by user. For full support of this system implementation of following method is necessary:
    ///   <para>
    ///    <see cref="Effect.GetParameters"/> -  returns description of effect's parameters. 
    ///    </para>
    ///    <para>
    ///    <see cref="Effect.GetAllSettings"/> - returns current values of parameters as array of objects
    ///    </para>
    ///    <see cref="Effect.SetAllSettings"/> - sets given values to all parameters
    ///    <para>
    ///    <see cref="Effect.SetSetting"/> - sets one parameter setting.
    ///    </para>
    ///    <para>
    ///      Remeber also to raise <see cref="E:ffect.SettingsChanged"/> event when user changes any of parameter that should be recorded.
    ///      Note that order of parameters in arrays returned by <see cref="Effect.GetParameters"/>, <see cref="Effect.GetAllSettings"/> and
    ///      passed to the <see cref="Effect.SetAllSettings"/> must be the same.
    ///   </para>
    ///  </para>
    /// </remarks>
    public abstract partial class Effect : UserControl
    {
        /// <summary>
        ///  Dumy constructor. Class is abstract.
        /// </summary>
        public Effect()
        {
            InitializeComponent();
        }

        #region signal_processing

        /// <summary>
        ///  Process input signal and modifies it.
        /// </summary>
        /// <param name="data"> Array containing input signal. </param>
        /// <returns> Modified output signal </returns>
        public virtual double[] effect(double[] data)
        {
            return data;
        }

        /// <summary>
        ///  Resets the effect.
        /// </summary>
        public virtual void Reset()
        { }

        #endregion

        #region effect_properties
        // various technical properties for 

        /// <summary>
        ///  Sampling rate of the input signal.
        /// </summary>
        protected uint sampleRate;

        /// <summary>
        ///  Gets or sets the sampling rate of the input signal passed to the effect.
        /// </summary>
        public virtual uint SampleRate
        {
            get { return sampleRate; }
            set { sampleRate=value; }
        }

        /// <summary>
        ///  Length of the array passed to the <see cref="Effect.effect"/> method.
        /// </summary>
        protected int dataSize;

        /// <summary>
        ///  Gets or sets data size expected by <see cref="Effect.effect"/> method.
        /// </summary>
        public virtual int DataSize
        {
            get { return dataSize; }
            set { dataSize = value; }
        }

        /// <summary>
        ///  Inidicates whether effect is switched on. True - on, false - bypassed.
        /// </summary>
        protected bool switchedOn;

        /// <summary>
        ///  Turns the effect on/off. True - on, false - on
        /// </summary>
        public virtual bool Switch
        {
            get { return switchedOn; }
            set { switchedOn = value; }
        }

        /// <summary>
        ///  Indicates whether the effect is selected.
        /// </summary>
        protected bool selected;

        /// <summary>
        ///  Gets or sets the selected indicator.
        /// </summary>
        public bool Selected
        {
            get { return selected; }
            set
            {
                selected = value;
                this.Refresh();
            }
        }

        /// <summary>
        ///  Gets the name of the effect.
        /// </summary>
        /// <remarks> 
        /// It is necesarry to override this property. It will be used to differ the effect from others
        /// </remarks>
        public virtual String Name
        {
            get { return "Base class for all effects"; }
        }

        #endregion

        #region painting

        /// <summary>
        ///  Brush used to highlight selected effect.
        /// </summary>
        protected HatchBrush SelectedBrush = new HatchBrush(HatchStyle.SmallConfetti, Color.DarkBlue,Color.Transparent);

        /// <summary>
        ///   Paints the effects.  
        /// </summary>
        /// <param name="e"> Paint event arguments </param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (selected)
                e.Graphics.FillRectangle(SelectedBrush, 0, 0, Width, Height);
        }

        #endregion

        #region settings
        // settings stuff

        /// <summary>
        ///  Gets array describing all parameters of the effect.
        /// </summary> 
        /// <returns> Array describing parameters </returns>
        public virtual EffectParam[] GetParameters()
        {
            return null;
        }

        /// <summary>
        ///  Gets all current parameter settings of the effect.
        /// </summary>
        /// <returns> All current parameter settings </returns>
        /// <remarks>
        ///  Effect returns values of all it's parameters. The order of parameter in array must be the same as is returned by <see cref="Effect.GetParameters"/> method.
        /// </remarks>
        public virtual object[] GetAllSettings()
        {
            return null;
        }

        /* Method recreates effects settings from given instance of object */
        /// <summary>
        ///  Sets value to all parameters.
        /// </summary>
        /// <param name="settings"> Values of all parameters. </param>
        /// <remarks>
        ///  Given array is expected to be previously created by <see cref="Effect.GetAllSettings"/>, so it must have the same order of parameters as returned in <see cref="Effect.GetParameters"/>
        /// </remarks>
        public virtual void SetAllSettings(object[] settings)
        { }

        /// <summary>
        ///  Occurs when user changes setting of any parameter.
        /// </summary>
        public virtual event EffectSettingsChangedDelegate SettingsChanged;

        /// <summary>
        ///  Adjusts parameter according to given settings event arguments.
        /// </summary>
        /// <param name="e"> Event argumetns describing parameter settings chagne. </param>
        /// <remarks>
        ///   Method is called from outside and is given an instance of SettingsChangedEventArgs that has been previously created by an effect of same type. 
        ///   So effect understand the mening of its Key and Value properties and should acordingly change ist settings.
        ///   Effect MUST NOT raise SettingsChanged event when this method is called. It would cause duplicating of the settings events.
        /// </remarks>
        public virtual void SetSetting(EffectSettingsChangedEventArgs e)
        { }

        #endregion
    }
}
